using System;
using System.Collections.Specialized;
using ChessKit.Common;

namespace ChessKit.ChessLogics
{
  public struct Move : IEquatable<Move>
  {
    private static readonly BitVector32.Section FromXSection = BitVector32.CreateSection(7);
    private static readonly BitVector32.Section FromYSection = BitVector32.CreateSection(7, FromXSection);
    private static readonly BitVector32.Section ToXSection = BitVector32.CreateSection(7, FromYSection);
    private static readonly BitVector32.Section ToYSection = BitVector32.CreateSection(7, ToXSection);
    private static readonly BitVector32.Section PromotionSection = BitVector32.CreateSection(6, ToYSection);
    private static readonly BitVector32.Section SideSection = BitVector32.CreateSection(1, PromotionSection);
    private static readonly BitVector32.Section MoveTypeSection = BitVector32.CreateSection(1, SideSection);
    // --------- total of 16 bits
    private static readonly BitVector32.Section ValidationProgressSection = BitVector32.CreateSection(3, MoveTypeSection);
    private static readonly BitVector32.Section ValidationWarningSection = BitVector32.CreateSection(7, ValidationProgressSection);
    private static readonly BitVector32.Section ValidationErrorSection = BitVector32.CreateSection(0x20, ValidationWarningSection);
//    private static readonly BitVector32.Section HintSection = BitVector32.CreateSection(0x400, ValidationErrorSection);

    private BitVector32 _data;

    public Position From
    {
      get { return new Position(_data[FromXSection], _data[FromYSection]); }
      private set
      {
        _data[FromXSection] = value.X;
        _data[FromYSection] = value.Y;
      }
    }
    public Position To
    {
      get { return new Position(_data[ToXSection], _data[ToYSection]); }
      private set
      {
        _data[ToXSection] = value.X;
        _data[ToYSection] = value.Y;
      }
    }
    public PieceType ProposedPromotion
    {
      get { return (PieceType) _data[PromotionSection]; }
      private set { _data[PromotionSection] = (int)value; }
    }
    public MoveType Type
    {
      get { return (MoveType)_data[MoveTypeSection]; }
      private set { _data[MoveTypeSection] = (int)value; }
    }

    public ValidationProgress ValidationProgress
    {
      get { return (ValidationProgress)_data[ValidationProgressSection]; }
      set { _data[ValidationProgressSection] = (int)value; }
    }
    public ValidationWarning Warning
    {
      get { return (ValidationWarning)_data[ValidationWarningSection] ; }
      set { _data[ValidationWarningSection] = (int)value ; }
    }
    public ValidationError Error
    {
      get { return (ValidationError)(_data[ValidationErrorSection]); }
      set { _data[ValidationErrorSection] = (int)value; }
    }

    public MoveHint Hint { get; set; }
//    {
//      get { return (MoveHint)_data[HintSection]; }
//      set { _data[HintSection] = (int)value; }
//    }
    
    public bool IsProposedPromotion { get { return ProposedPromotion != 0; } }
    // This is a bad idea for MoveType.Resign move for example
    //public bool IsEmpty { get { return From == To; } }

    public Move(MoveType type) 
      : this()
    {
      Type = type;
    }
    public Move(Position from, Position to) 
      : this(from, to, 0)
    {
    }
    public Move(Position from, Position to, PieceType promotion)
      : this()
    {
      From = from;
      To = to;
      ProposedPromotion = promotion;
    }

    #region ' Equality '

    public bool Equals(Move other)
    {
      return other.From.Equals(From) && other.To.Equals(To) && Equals(other.ProposedPromotion, ProposedPromotion);
    }

    public override bool Equals(object obj)
    {
      if (ReferenceEquals(null, obj)) return false;
      if (obj.GetType() != typeof (Move)) return false;
      return Equals((Move) obj);
    }

    public override int GetHashCode()
    {
      unchecked
      {
        int result = From.GetHashCode();
        result = (result*397) ^ To.GetHashCode();
        result = (result*397) ^ ProposedPromotion.GetHashCode();
        return result;
      }
    }

    public static bool operator ==(Move left, Move right)
    {
      return left.Equals(right);
    }

    public static bool operator !=(Move left, Move right)
    {
      return !left.Equals(right);
    }


    #endregion

    public override string ToString()
    {
      return string.Format("{0}-{1}", From, To);
    }
  }
}